FormBuilder = {}

FormBuilder.Obj = Obj
_G.Obj = nil

FormBuilder.ExposeObj = function() _G.Obj = FormBuilder.Obj end

FormBuilder.Forms = {}

FormBuilder.Run = function() FormBuilder.Obj.Loop() end

FormBuilder.RGB = function(r,g,b) return Bit.Or(r,Bit.Shl(g,8),Bit.Shl(b,16)) end

FormBuilder.RemoveChild = function(form) --lol abortion
	FormBuilder.DebugMessage("FormBuilder.RemoveChild: Removing form " .. tostring(form) .. " from main collection")
	for i=#FormBuilder.Forms,1,-1 do
		if FormBuilder.Forms[i] == form then
			table.remove(FormBuilder.Forms,i)
			form.Parent = nil
			if not form.IsDisposed then
				form.Dispose()
			end
		end
	end
	if #FormBuilder.Forms == 0 then
		FormBuilder.DebugMessage("FormBuilder.RemoveChild: No forms left, executing Obj.Exit()")
		FormBuilder.Obj.Exit()
	end
end

FormBuilder.Error = function(message,level) 
	error("FormBuilder: " .. message,level)
end

FormBuilder.DebugMessage = function(s) if FormBuilder.Debug == true then print("FormBuilder[DEBUG]: " .. s) end end

FormBuilder.EventTypes = { "OnClick", "OnDblClick", "OnEnter", "OnExit", "OnChange", "OnSelect", "OnActivate", "OnClose", "OnPaint", "OnMouseEnter", "OnMouseLeave", "OnPopup", "OnSpecialLineColors", "OnTimer" }

FormBuilder.Enhance = function(formobject,objectname)
	local debugPrint = function(s) FormBuilder.DebugMessage(s) end
	local pParent = nil
	
	rawset(formobject,"Name",objectname)
	rawset(formobject,"IsDisposed",false)
	rawset(formobject,"Self",formobject)
	rawset(formobject,"Children",{})
	rawset(formobject,"Add",function(objtype,name)
		debugPrint("Add: Adding <" .. objtype .. "> '" .. name .. "' to parent " .. tostring(formobject))
		if ( objtype == nil and name == nil ) or ( objtype ~= nil and name == nil ) then
			FormBuilder.Error("Improper function call. Syntax: formobject.Add(<FormObjectType>,<Name>)",3)
		end
		local pObjCreated, pChildObject = pcall(FormBuilder.Obj.Create,objtype)
		if pObjCreated and pChildObject ~= nil then
			debugPrint("Add: <" .. objtype .. "> '" .. name .. "' created, adding extension methods")
			FormBuilder.Enhance(pChildObject,name)
			pChildObject.Parent = formobject
			table.insert(formobject.Children,pChildObject)
			debugPrint("Add: " .. tostring(pChildObject) .. " extended and added to Parent " .. tostring(formobject))
			return pChildObject
		end
		FormBuilder.Error("Add: Can't add child <" .. objtype .. "> '" .. name .. "' to parent " .. tostring(formobject) .. ". Illegal type <" .. objtype .. ">",4)
		return nil
	end)
	
	rawset(formobject,"HasChild",function(childobject)
		for k,v in ipairs(formobject.Children) do
			if v == childobject then
				return k
			end
		end
		return nil
	end)
	
	rawset(formobject,"RemoveChild",function(childobject)
		local key = formobject.HasChild(childobject)
		if key ~= nil then
			table.remove(formobject.Children,key)
			childobject.Parent = nil
			return childobject
		end
		return nil
	end)
	
	rawset(formobject,"DisposeChild",function(childobject)
		local child = formobject.RemoveChild(childobject)
		if child ~= nil then
			child.Dispose()
		end
		return false
	end)
	
	rawset(formobject,"FindChildByName",function(childname)
		for i=#formobject.Children,1,-1 do
			if formobject.Children[i].Name == childname then
				return formobject.Children[i]
			end
		end
		return nil
	end)
	
	rawset(formobject,"ClearEvents",function()
		for i=1,#FormBuilder.EventTypes,1 do
			local success, result = pcall(function() return formobject[FormBuilder.EventTypes[i]] end)
			if success and result ~= nil then
				formobject[FormBuilder.EventTypes[i]] = nil
				if success then debugPrint("ClearEvents: Removed event " .. FormBuilder.EventTypes[i] .. " from " .. tostring(formobject)) end
			end
		end
	end)
	
	rawset(formobject,"Dispose",function()
		debugPrint("Dispose: I'm disposing of " .. tostring(formobject) .. " with parent " .. tostring(formobject.Parent))
		rawset(formobject,"IsDisposed",true)
		for i=#formobject.Children,1,-1 do
			formobject.Children[i].Dispose()
		end
		if formobject.Parent ~= nil and type(formobject.Parent) == "table" then
			formobject.Parent.RemoveChild(formobject)
		end
		formobject.ClearEvents()
		debugPrint("Dispose: " .. tostring(formobject) .. " disposed")
		FormBuilder.Obj.Free(formobject)
	end)
	
	local pShow = formobject.Show
	rawset(formobject,"Show",function()
		pShow()
	end)
	
	local eventHandlers = {}
	local eventHandlerCallbacks = {}
	
	local handleEvent = function(caller,eventtype)
		handlers = eventHandlers[eventtype]
		if handlers ~= nil then
			for i=#handlers,1,-1 do
				handlers[i](caller)
			end
		end
	end
	
	for i=1,#FormBuilder.EventTypes do
		eventHandlerCallbacks[FormBuilder.EventTypes[i]] = function(caller) handleEvent(caller, FormBuilder.EventTypes[i]) end
	end
	
	local addEvent = function(ft,eventtype)
		if eventHandlers[eventtype] == nil then eventHandlers[eventtype] = {} end
		table.insert(eventHandlers[eventtype],ft)
		formobject[eventtype] = eventHandlerCallbacks[eventtype]
	end
	
	local removeEvent = function(ft,eventtype)
		local handlers = eventHandlers[eventtype]
		if handlers ~= nil then
			for i=#handlers,1,-1 do
				if handlers[i] == ft then
					table.remove(handlers,i)
				end
			end
		end
	end
	
	local clearEvents = function(eventtype)
		eventHandlers[eventtype] = nil
	end
		
	local mt = getmetatable(formobject)
	local fbmt = {}
	fbmt.__index = function(t,k)
		value = nil
		addtype = string.match(k,"^Add(%a+)")
		if addtype ~= nil then
			value = function(name) return formobject.Add("T" .. addtype,name) end
		end
		if value == nil and not formobject.IsDisposed then
			for i=1,#FormBuilder.EventTypes,1 do
				if k == FormBuilder.EventTypes[i] then
					value = { 
						Handlers = eventHandlers[FormBuilder.EventTypes[i]],
						Add = function(ft) addEvent(ft,FormBuilder.EventTypes[i]) end,
						Remove = function(ft) removeEvent(ft,FormBuilder.EventTypes[i]) end
					}
				end
			end
		end
		if value == nil and k == "Parent" then value = pParent end --weird behavior, can't rawset parent. guess it's really not that weird though
		if value == nil then value = formobject.FindChildByName(k) end
		if value == nil then return mt.__index(t,k) end
		return value
	end
	fbmt.__newindex = function(t,k,v)
		if k == "Parent" then 
			pParent = v 
			if v == FormBuilder then return end
		end
		if k == "Color" and type(v) == "table" and #v >= 3 then
			v = FormBuilder.RGB(v[1],v[2],v[3])
		end
		for i=1,#FormBuilder.EventTypes,1 do
			if k == FormBuilder.EventTypes[i] then
				FormBuilder.DebugMessage("Events: Setting " .. tostring(formobject) .. " event handler for " .. FormBuilder.EventTypes[i] .. " to " .. tostring(v))
				if v == nil then
					eventHandlers[FormBuilder.EventTypes[i]] = nil
				else
					if v ~= eventHandlerCallbacks[FormBuilder.EventTypes[i]] then
						eventHandlers[FormBuilder.EventTypes[i]] = {v}
					end
					v = eventHandlerCallbacks[FormBuilder.EventTypes[i]]
				end
			end
		end
		mt.__newindex(t,k,v) 
	end
	fbmt.__tostring = function(t) return "<" .. t.ClassName .. "> '" .. t.Name .. "'" end
	setmetatable(formobject,fbmt)
end

FormBuilder.New = function(formname)
	FormBuilder.DebugMessage("New: Creating new <TForm> '" .. formname .. "'")
	local pForm = FormBuilder.Obj.Create("TForm")
	FormBuilder.Enhance(pForm,formname)
	pForm.Parent = FormBuilder
	table.insert(FormBuilder.Forms,pForm)
	pForm.OnClose = function() pForm.Hide() pForm.Dispose() end
	return pForm
end

